package com.linkgie.galaxyframework.crypto.spi;

import java.io.IOException;
import java.io.OutputStream;

import com.linkgie.galaxyframework.crypto.CryptoException;

/**
 * 加密解密操作；
 * 
 * @author liso
 *
 * @param <E> 加密密钥的类型；
 * @param <D> 解密密钥的类型；
 */
public interface CipherFuncSPI<E, D> extends CryptoFuncSPI {

	/**
	 * 加密数据；
	 * 
	 * @param pubKey     公钥；
	 * @param plainBytes 明文数据；
	 * @return 密文数据；
	 */
	default byte[] encrypt(E key, byte[] plainBytes) {
		return encrypt(key, plainBytes, 0, plainBytes.length);
	}

	/**
	 * 加密数据；
	 * 
	 * @param pubKey     公钥；
	 * @param plainBytes 明文数据；
	 * @param offset     明文输入偏移；
	 * @param length     明文输入长度；
	 * @return 密文数据；
	 */
	byte[] encrypt(E key, byte[] plainBytes, int offset, int length);

	/**
	 * 加密数据；
	 * 
	 * @param pubKey     公钥；
	 * @param plainBytes 明文数据；；
	 * @param offset     明文输入偏移；
	 * @param length     明文输入长度；
	 * @param out        密文输出；
	 * @return 密文输出长度；
	 */
	default int encrypt(E key, byte[] plainBytes, int offset, int length, OutputStream out) {
		byte[] cipherBytes = encrypt(key, plainBytes, offset, length);
		try {
			out.write(cipherBytes);
			return cipherBytes.length;
		} catch (IOException e) {
			throw new CryptoException(e.getMessage(), e);
		}
	}

	/**
	 * 加密数据；
	 * 
	 * @param rawKey     未编码的原加密密钥；
	 * @param plainBytes 明文数据；
	 * @return 密文数据；
	 */
	default byte[] encrypt(byte[] rawKey, byte[] plainBytes) {
		return encrypt(rawKey, plainBytes, 0, plainBytes.length);
	}

	/**
	 * 加密数据；
	 * 
	 * @param rawKey     未编码的原加密密钥；
	 * @param plainBytes 明文数据；
	 * @param offset     明文输入偏移；
	 * @param length     明文输入长度；
	 * @return 密文数据；
	 */
	byte[] encrypt(byte[] rawKey, byte[] plainBytes, int offset, int length);

	/**
	 * 加密数据；
	 * 
	 * @param rawKey     未编码的原加密密钥；
	 * @param plainBytes 明文数据；；
	 * @param offset     明文输入偏移；
	 * @param length     明文输入长度；
	 * @param out        密文输出；
	 * @return 密文输出长度；
	 */
	default int encrypt(byte[] rawKey, byte[] plainBytes, int offset, int length, OutputStream out) {
		byte[] cipherBytes = encrypt(rawKey, plainBytes, offset, length);
		try {
			out.write(cipherBytes);
			return cipherBytes.length;
		} catch (IOException e) {
			throw new CryptoException(e.getMessage(), e);
		}
	}

	/**
	 * 解密数据；
	 * 
	 * @param DK          解密密钥；
	 * @param cipherBytes 密文数据；
	 * @return 明文数据；
	 */
	default byte[] decrypt(D key, byte[] cipherBytes) {
		return decrypt(key, cipherBytes, 0, cipherBytes.length);
	}

	/**
	 * 解密数据；
	 * 
	 * @param DK          解密密钥；
	 * @param cipherBytes 密文数据；
	 * @param offset      密文输入偏移；
	 * @param length      密文输入长度；
	 * @return 明文数据；
	 */
	byte[] decrypt(D key, byte[] cipherBytes, int offset, int length);

	/**
	 * 解密数据；
	 * 
	 * @param DK          解密密钥；
	 * @param cipherBytes 密文数据；
	 * @param offset      密文输入偏移；
	 * @param length      密文输入长度；
	 * @param out         明文输出；
	 * @return 输出长度；
	 */
	default int decrypt(D key, byte[] cipherBytes, int offset, int length, OutputStream out) {
		byte[] plainBytes = decrypt(key, cipherBytes, offset, length);
		try {
			out.write(plainBytes);
			return plainBytes.length;
		} catch (IOException e) {
			throw new CryptoException(e.getMessage(), e);
		}
	}

	/**
	 * 解密数据；
	 * 
	 * @param rawKey      未编码的原解密密钥；
	 * @param cipherBytes 密文数据；
	 * @return 明文数据；
	 */
	default byte[] decrypt(byte[] rawKey, byte[] cipherBytes) {
		return decrypt(rawKey, cipherBytes, 0, cipherBytes.length);
	}

	/**
	 * 解密数据；
	 * 
	 * @param rawKey      未编码的原解密密钥；
	 * @param cipherBytes 密文数据；
	 * @param offset      密文输入偏移；
	 * @param length      密文输入长度；
	 * @return 明文数据；
	 */
	byte[] decrypt(byte[] rawKey, byte[] cipherBytes, int offset, int length);

	/**
	 * 解密数据；
	 * 
	 * @param rawKey      未编码的原解密密钥；
	 * @param cipherBytes 密文数据；
	 * @param offset      密文输入偏移；
	 * @param length      密文输入长度；
	 * @param out         明文输出；
	 * @return 输出长度；
	 */
	default int decrypt(byte[] rawKey, byte[] cipherBytes, int offset, int length, OutputStream out) {
		byte[] plainBytes = decrypt(rawKey, cipherBytes, offset, length);
		try {
			out.write(plainBytes);
			return plainBytes.length;
		} catch (IOException e) {
			throw new CryptoException(e.getMessage(), e);
		}
	}

}
